package com.binance.autojob.utils;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotatedElementUtils;

import com.binance.autojob.annotation.MyScheduledJob;



public class JobMethodAnnotionPostProcessor implements BeanPostProcessor, Ordered {
    private static Logger logger = LoggerFactory.getLogger(JobMethodAnnotionPostProcessor.class);
    private final Set<Class<?>> nonAnnotatedClasses =
            Collections.newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>(64));

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
        if (!this.nonAnnotatedClasses.contains(targetClass)
                && targetClass.getAnnotation(MyScheduledJob.class) == null) {
            Map<Method, Set<MyScheduledJob>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
                    new MethodIntrospector.MetadataLookup<Set<MyScheduledJob>>() {
                        @Override
                        public Set<MyScheduledJob> inspect(Method method) {
                            Set<MyScheduledJob> scheduledMethods =
                                    AnnotatedElementUtils.getAllMergedAnnotations(method, MyScheduledJob.class);
                            return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
                        }
                    });
            if (annotatedMethods.isEmpty()) {
                this.nonAnnotatedClasses.add(targetClass);
            } else {
                // Non-empty set of methods
                for (Map.Entry<Method, Set<MyScheduledJob>> entry : annotatedMethods.entrySet()) {
                    Method method = entry.getKey();
                    for (MyScheduledJob scheduled : entry.getValue()) {
                        ExecutorExt.regist(bean, method, scheduled);
                    }
                }
                logger.info(annotatedMethods.size() + " @MyScheduledJob methods processed on bean '" + beanName + "': "
                        + annotatedMethods);
            }
        }
        return bean;
    }


    @Override
    public int getOrder() {
        return Integer.MIN_VALUE;
    }
}
